#include <stdio.h>
#include <iostream>
#include "RelationshipTable.h"



RelationshipTable::RelationshipTable(void) {

	/*
	// Initialize the argument vector maps
	for (int i = 0; i < MAX_ARGUMENTS; i++) {
		std::map<RelationshipType, std::vector<RelationshipArgumentType>> argVectorMap;
		mArgumentVectorMaps[i] = argVectorMap;
	}
	*/
	initialize();
}


RelationshipTable::~RelationshipTable(void) {
}

// Initialize RelationshipTable by adding relationship constraints (Follows, Follows*, Parent, Parent*)
bool RelationshipTable::initialize(void) {

	RelationshipArgumentType affectsRef[] = {INTEGER, SYNONYM_ASSIGN, SYNONYM_STMT, SYNONYM_PROGLINE};
	RelationshipArgumentType lineRef[] = {INTEGER, SYNONYM_ASSIGN, SYNONYM_CALL, SYNONYM_WHILE, SYNONYM_STMT, SYNONYM_IF, SYNONYM_PROGLINE};
	RelationshipArgumentType entRef[] = {STRING, SYNONYM_PROCEDURE};
	RelationshipArgumentType varRef[] = {SYNONYM_VARIABLE, STRING};
	RelationshipType line_line[] = { NEXTBIP, NEXTBIP_STAR, AFFECTSBIP, AFFECTSBIP_STAR, PARENT, PARENT_STAR, FOLLOWS, FOLLOWS_STAR, NEXT, NEXT_STAR, AFFECTS, AFFECTS_STAR };
	RelationshipType affects[] = { AFFECTSBIP, AFFECTSBIP_STAR, AFFECTS, AFFECTS_STAR };
	std::map<relTypePair, bool> affects_map;
	std::map<relTypePair, bool> line_line_map;
	std::map<relTypePair, bool> ent_var_map;
	std::map<relTypePair, bool> line_var_map;
	std::map<relTypePair, bool> ent_ent_map;
	std::map<relTypePair, bool> contain_map;
	std::map<relTypePair, bool> contain_star_map;	
	RelationshipArgumentType container_lhs[] = {SYNONYM_WHILE,SYNONYM_IF, SYNONYM_STMT, INTEGER, SYNONYM_PROGLINE};
	RelationshipArgumentType big_container_lhs[] = {SYNONYM_STMTLST, SYNONYM_PROCEDURE};
	RelationshipArgumentType stmtLst_rhs[] =  {SYNONYM_WHILE, SYNONYM_IF, SYNONYM_ASSIGN, SYNONYM_CALL, SYNONYM_STMT, SYNONYM_PROGLINE, INTEGER};
	RelationshipArgumentType assign_lhs[] = {SYNONYM_ASSIGN, SYNONYM_STMT, INTEGER, SYNONYM_PROGLINE, SYNONYM_PLUS, SYNONYM_MINUS, SYNONYM_TIMES};
	RelationshipArgumentType assign_rhs[] = {SYNONYM_VARIABLE, SYNONYM_CONSTANT, SYNONYM_PLUS, SYNONYM_MINUS, SYNONYM_TIMES};

	contain_map.insert(createPair(SYNONYM_PROCEDURE, SYNONYM_STMTLST));

	for (auto it2 = std::begin(stmtLst_rhs); it2 != std::end(stmtLst_rhs); ++it2) {
		contain_map.insert(createPair(SYNONYM_STMTLST, *it2));
	}

	for (auto it1 = std::begin(container_lhs); it1 != std::end(container_lhs); ++it1) {
		contain_map.insert(createPair(*it1, SYNONYM_VARIABLE));
		contain_map.insert(createPair(*it1, SYNONYM_STMTLST));

		// for while if equivalent synonyms, their rhs can be stmtLst_rhs or assign_rhs for contain*
		for (auto it2 = std::begin(stmtLst_rhs); it2 != std::end(stmtLst_rhs); ++it2) {
			contain_star_map.insert(createPair(*it1, *it2));
		}
		for (auto it2 = std::begin(assign_rhs); it2 != std::end(assign_rhs); ++it2) {
			contain_star_map.insert(createPair(*it1, *it2));
		}

	}

	for (auto it1 = std::begin(big_container_lhs); it1 != std::end(big_container_lhs); ++it1) {
		for (auto it2 = std::begin(stmtLst_rhs); it2 != std::end(stmtLst_rhs); ++it2) {
			contain_map.insert(createPair(*it1, *it2));
		}
		for (auto it2 = std::begin(assign_rhs); it2 != std::end(assign_rhs); ++it2) {
			contain_star_map.insert(createPair(*it1, *it2));
		}
	}
	
	for (auto it1 = std::begin(assign_lhs); it1 != std::end(assign_lhs); ++it1) {
		for (auto it2 = std::begin(assign_rhs); it2 != std::end(assign_rhs); ++it2) {
			contain_map.insert(createPair(*it1, *it2));
			contain_star_map.insert(createPair(*it1, *it2));
		}
	}
	
	//contains* (from most general to most strict)
	// procedure -- stmtLst_RHS + assign_RHS
	// stmtLst -- (procedure's RHS)
	// while/if/integer/stmt/progline: same as stmtLst
	// assign: assign_RHS
	// symbols: assign_RHS
	// call: nothing

	for (auto it1 = std::begin(affectsRef); it1 != std::end(affectsRef); ++it1) {
		for (auto it2 = std::begin(affectsRef); it2 != std::end(affectsRef); ++it2) {
			affects_map.insert(createPair(*it1, *it2));
		}
	}

	for (auto it1 = std::begin(lineRef); it1 != std::end(lineRef); ++it1) {
		for (auto it2 = std::begin(lineRef); it2 != std::end(lineRef); ++it2) {
			line_line_map.insert(createPair(*it1, *it2));
		}
		
		for (auto it2 = std::begin(varRef); it2 != std::end(varRef); ++it2) {
			line_var_map.insert(createPair(*it1, *it2));
		}
	}

	for (auto it1 = std::begin(entRef); it1 != std::end(entRef); ++it1) {
		for (auto it2 = std::begin(varRef); it2 != std::end(varRef); ++it2) {
			ent_var_map.insert(createPair(*it1, *it2));
		}

		for (auto it2 = std::begin(entRef); it2 != std::end(entRef); ++it2) {
			ent_ent_map.insert(createPair(*it1, *it2));
		}
	}

	for (auto it = std::begin(line_line); it !=std::end(line_line); ++it) {
		insertValidRelationsForType(*it, line_line_map);
	}

	for (auto it = std::begin(affects); it !=std::end(affects); ++it) {
		insertValidRelationsForType(*it, affects_map);
	}

	insertValidRelationsForType(MODIFIES_PROC, ent_var_map);
	insertValidRelationsForType(USES_PROC, ent_var_map);
	insertValidRelationsForType(MODIFIES_LINE, line_var_map);
	insertValidRelationsForType(USES_LINE, line_var_map);
	insertValidRelationsForType(CALLS, ent_ent_map);
	insertValidRelationsForType(CALLS_STAR, ent_ent_map);
	insertValidRelationsForType(CONTAINS, contain_map);
	insertValidRelationsForType(CONTAINS_STAR, contain_star_map);


	return true;

}


void RelationshipTable::insertValidRelationsForType(RelationshipType relationshipType, std::map<relTypePair, bool> map) {
	mValidRelEntryMap.insert(std::pair<RelationshipType,std::map<relTypePair, bool>> (relationshipType, map));

}
bool RelationshipTable::isValidArgument(RelationshipType relationshipType, RelationshipArgumentType arg1Type, RelationshipArgumentType arg2Type) {
	std::map<RelationshipType, std::map<relTypePair, bool>>::const_iterator it = mValidRelEntryMap.find(relationshipType);
	if (it == mValidRelEntryMap.end()) return false;
	std::map<relTypePair, bool> map = it->second;
	return (map.find(relTypePair(arg1Type, arg2Type))!=map.end());
}

std::pair<RelationshipTable::relTypePair, bool> RelationshipTable::createPair(RelationshipArgumentType arg1Type, RelationshipArgumentType arg2Type) {
	return std::pair<relTypePair, bool> (relTypePair(arg1Type, arg2Type), true);
}


